﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Configuration;
using StyleMVVM.Data.Activation;
using StyleMVVM.DependencyInjection;
using StyleMVVM.Utilities;
#if !DOT_NET
using Windows.Foundation;

#endif

namespace StyleMVVM.Logging
{
	public sealed class DefaultLog : ILog, IDependencyInjectionContainerAware, IActivationAware
	{
		private readonly object lockObject = new object();
		private volatile bool configuring = false;

		public static void RegisterExport(IDependencyInjectionContainer container)
		{
			container.Register(typeof(DefaultLog)).
			          As(typeof(ILog)).
			          AndSharedPermenantly().
			          WithPriority(-1);
		}

		public IConfigurationService ConfigurationService { get; private set; }

		public IList<ILogAppender> Appenders { get; private set; }

		public IDependencyInjectionContainer Container { get; set; }

		public void Activate()
		{
			ConfigurationService =
				Container.LocateByType(typeof(IConfigurationService)) as IConfigurationService;

			Appenders = new List<ILogAppender>();

			foreach (ILogAppender logAppender in Container.LocateAllByType(typeof(ILogAppender)))
			{
				Appenders.Add(logAppender);
			}
		}

#if NETFX_CORE
		public IAsyncOperation<bool> Configure()
		{
			return internalConfigure().AsAsyncOperation();
		}
#else
		public Task<bool> Configure()
		{
			return internalConfigure();
		}
#endif

		private async Task<bool> internalConfigure()
		{
			if (!configuring)
			{
				bool setConfiguring = false;

				lock (lockObject)
				{
					if (!configuring)
					{
						setConfiguring = true;
						configuring = true;
					}
				}

				if (setConfiguring)
				{
					var useFileLog = (bool)await ConfigurationService.GetValue("UseFileLogAppender",
					                                                           typeof(bool),
					                                                           true);

					if (DesignMode.IsEnabled)
					{
						LoggingLevel = LogLevel.DEBUG;
					}
					else
					{
						LoggingLevel = (LogLevel)await ConfigurationService.GetValue("LogLevel",
						                                                             typeof(LogLevel),
						                                                             LogLevel.INFO);
					}

					for (int i = 0; i < Appenders.Count;)
					{
						if (!useFileLog && Appenders[i].GetType() == typeof(FileLogAppender))
						{
							Appenders.RemoveAt(i);
						}
						else
						{
							Appenders[i].LoggingLevel = LoggingLevel;

							try
							{
								await Appenders[i].Configure();
							}
							catch (Exception)
							{
							}

							i++;
						}
					}

					return true;
				}
			}

			return false;
		}

		public LogLevel LoggingLevel { get; set; }

		public void Flush()
		{
			foreach (ILogAppender logAppender in Appenders)
			{
				logAppender.Flush();
			}
		}

		public void Debug(string message)
		{
			Debug(message, null, null);
		}

		public void Debug(string message, string supplemental)
		{
			Debug(message, supplemental, null);
		}

		public void Debug(string message, string supplemental, Exception exp)
		{
			Log(
				new LogEntry
					{
						Level = LogLevel.DEBUG,
						Message = message,
						Supplemental = supplemental,
						Exception = exp
					});
		}

		public void Info(string message)
		{
			Info(message, null, null);
		}

		public void Info(string message, string supplemental)
		{
			Info(message, supplemental, null);
		}

		public void Info(string message, string supplemental, Exception exp)
		{
			Log(
				new LogEntry
					{
						Level = LogLevel.INFO,
						Message = message,
						Supplemental = supplemental,
						Exception = exp
					});
		}

		public void Warn(string message)
		{
			Warn(message, null, null);
		}

		public void Warn(string message, string supplemental)
		{
			Warn(message, supplemental, null);
		}

		public void Warn(string message, string supplemental, Exception exp)
		{
			Log(
				new LogEntry
					{
						Level = LogLevel.WARN,
						Message = message,
						Supplemental = supplemental,
						Exception = exp
					});
		}

		public void Error(string message)
		{
			Error(message, null, null);
		}

		public void Error(string message, string supplemental)
		{
			Error(message, supplemental, null);
		}

		public void Error(string message, string supplemental, Exception exp)
		{
			Log(
				new LogEntry
					{
						Level = LogLevel.ERROR,
						Message = message,
						Supplemental = supplemental,
						Exception = exp
					});
		}

		public void Fatal(string message)
		{
			Fatal(message, null, null);
		}

		public void Fatal(string message, string supplemental)
		{
			Fatal(message, supplemental, null);
		}

		public void Fatal(string message, string supplemental, Exception exp)
		{
			Log(
				new LogEntry
					{
						Level = LogLevel.FATAL,
						Message = message,
						Supplemental = supplemental,
						Exception = exp
					});
		}

		public void Log(LogEntry logEntry)
		{
			foreach (ILogAppender logAppender in Appenders)
			{
				try
				{
					logAppender.Log(logEntry);
				}
				catch (Exception exp)
				{
					// where do we go from here .....
				}
			}
		}
	}
}